大家好,今天繼續來開發元件,並動手解決實務上我們遇到的設定配置的問題。在昨天的練習裡,我們可以使用babal-plugin-import按需載入使用到的第三方UI元件的css,之後使用yarn start啟動react的web,並在瀏覽器顯示正確的結果,但其實在Webview裡,這樣設定仍有些地方需要調整。
在之前的Webview單元我們提到,VSCode的WebView裡要訪問script的資源跟css時有限制,要將script跟css邊謙裡的url專換為webview專用的Uri才能順利生效。
在沒有使用rewired多import其他ui的library前,使用Create React App生成的的asset-manifest.json是這個樣子,如下所示,js檔案分為三支檔案。css有一支,對應到files屬性下的main.css路徑。
{
"files": {
"main.css": "./static/css/main.5f361e03.chunk.css",
...
},
"entrypoints": [
"static/js/runtime-main.e139f7bb.js",
"static/js/2.390c405e.chunk.js",
"static/css/main.5f361e03.chunk.css",
"static/js/main.2ba94f4f.chunk.js"
]
}
在webview專案底下使用rewired改寫過後的yarn build後,讓我們到out/build/asset-manifest.json底下查看,可以發現在多打包一個UI Library的css後,entrypoints下面的css檔案變為兩支,且有一支我們無法直接在files下面透過隨機產生的static/css的key值獲取正確路徑的css分塊檔案。
{
"files": {
"main.css": "./static/css/main.dc44bd41.chunk.css",
...
"static/css/2.438752f9.chunk.css": "./static/css/2.438752f9.chunk.css",
...
},
"entrypoints": [
"static/js/runtime-main.2206c2b6.js",
"static/css/2.438752f9.chunk.css",
"static/js/2.f197f274.chunk.js",
"static/css/main.dc44bd41.chunk.css",
"static/js/main.b3ed2647.chunk.js"
]
}
因此,當我們在WebviewPanel裡使用前面的loadWebViewContent方法時,我們讀取css檔案的底下這段
<link rel="stylesheet" type="text/css" href="${this.webviewUri(mainfest.files['main.css'])}">
路徑會失效。
這邊筆者處理被webpack分塊後的css檔案做法和前面處理分塊後的js檔案做法一樣。在我們的loadWebcontent方法裡,我們直接透過字串filter出我們的所有分塊過的css檔案。
const entrypointsCss = mainfest['entrypoints'].filter((p: string) => p.includes('static/css'));
之後,在樣板字串裡我們直接透過陣列index取得所有css的檔案路徑,並使用先前我們準備好的webviewUri轉換方法轉換css路徑,即可讓WebView正確的讀取所有我們所需的css檔案。
<link rel="stylesheet" type="text/css" href="${this.webviewUri(entrypointsCss[0])}">
<link rel="stylesheet" type="text/css" href="${this.webviewUri(entrypointsCss[1])}">
loadWebviewContent完整的精簡版方法如下所示:
class WebviewPanel {
...
public loadWebviewContent() {
const mainfest = readJSON(path.join(this.context.extensionPath, 'out/build/asset-manifest.json'));
const entrypointsJs = mainfest['entrypoints'].filter((p: string) => p.includes('static/js'));
const entrypointsCss = mainfest['entrypoints'].filter((p: string) => p.includes('static/css'));
return `<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" href="${this.webviewUri('./favicon.ico')}"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="theme-color" content="#000000"/>
<meta name="description" content="Web site created using create-react-app"/>
<title>React App</title>
<base href="${this.webviewUri('/')}">
<link rel="stylesheet" type="text/css" href="${this.webviewUri(mainfest.files['main.css'])}">
<link rel="stylesheet" type="text/css" href="${this.webviewUri(entrypointsCss[0])}">
<link rel="stylesheet" type="text/css" href="${this.webviewUri(entrypointsCss[1])}">
<link rel="manifest" href="${this.webviewUri('./manifest.json')}"/>
</head>
<body
<div id="root"></div>
<script src="${this.webviewUri(entrypointsJs[0])}"></script>
<script src="${this.webviewUri(entrypointsJs[1])}"></script>
<script src="${this.webviewUri(entrypointsJs[2])}"></script>
</body>
</html>`;
}
}
現在我們調整完了loadWebviewContent方法,再啟動extension,即會正確的在VSCode裡面顯示react使用第三方元件後的css的樣式。
在筆者上面的做法裡,需在build完後的asset-manifest.json裡檢查有幾隻切分出去的檔案,並手動套用到loadWebviewContent的html裡。但其實我們也可以使用另一種方式解決問題,在Open Source的世界裡,有一個vscode-webview-react的react vscode webview template專案,裡面使用了直接禁用webpack的split chunk設定的方式來解決問題。
在vscode-webview-react專案裡,有一支build-non-split.js檔,引入了rewired這個套件停用webpack的相關split chunk屬性。
#!/usr/bin/env node
// Disables code splitting into chunks
// See https://github.com/facebook/create-react-app/issues/5306#issuecomment-433425838
const rewire = require("rewire");
const defaults = rewire("react-scripts/scripts/build.js");
let config = defaults.__get__("config");
config.optimization.splitChunks = {
cacheGroups: {
default: false
}
};
config.optimization.runtimeChunk = false;
這裡我們可以參考相關的Wepack設定做配置。
在我們的專案裡,我們會在config-overrides.js下面使用不同套件做配置。
這裡筆者參考套件寫法的風格撰寫一個客製化的buildNonSplit方法進行webpack的設定,全部配置如下。
const {
override,
fixBabelImports
} = require('customize-cra');
const buildNonSplit = () => config => {
config.optimization.splitChunks = {
cacheGroups: {
default: false
}
};
config.optimization.runtimeChunk = false;
return config;
};
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd-mobile',
style: 'true',
}),
buildNonSplit()
);
2020/11/03更正: 閱讀套件程式後了解,customize-cra套件實際上有針對同一個issue提供禁用code split的工具方法,詳見Github連結,我們可以直接使用內建的disableChunk方法。
const { override, disableChunk, fixBabelImports } = require('customize-cra'); module.exports = override( fixBabelImports('import', { libraryName: 'antd-mobile', style: 'true', }), disableChunk() );
好的,現在我們使用yarn build,再到out/build/asset-manifest.json檔案下面查看結果,可以確認entrypoints下面僅有main.js與main.css兩隻檔案
```json=
{
"files": {
"main.css": "./static/css/main.e7c5d175.css",
"main.js": "./static/js/main.1d4ca48f.js",
"main.js.map": "./static/js/main.1d4ca48f.js.map",
"index.html": "./index.html",
"static/css/main.e7c5d175.css.map": "./static/css/main.e7c5d175.css.map",
"static/js/main.1d4ca48f.js.LICENSE.txt": "./static/js/main.1d4ca48f.js.LICENSE.txt"
},
"entrypoints": [
"static/css/main.e7c5d175.css",
"static/js/main.1d4ca48f.js"
]
}
現在在我們的loadWebviewContent方法裡,我們僅需簡單的引用files屬性下的main.js與main.css路徑即可讓Webview使用。
// 引入main.css
<link rel="stylesheet" type="text/css" href="${this.webviewUri(mainfest.files['main.css'])}">
...
// 引入main.js
<script src="${this.webviewUri(mainfest.files['main.js'])}"></script>
好的,今天我們花了些時間處理跟Create React App裡的Webpack設定,並將其套用到webview上。會讓人感到有些繁瑣,但這是實務開發上必然會遇到的過程。
在今天筆者也示範了兩種解決問題的方式,選用哪種方式處理引入的css或js檔案路徑,視乎專案是否需要Webpack的分塊與相關優化設定。Open Source世界裡的專案解法是我們很好的參考資源,但未必就會是當下最適合我們情境的解法。在我們理解背後相關原理後,我們可以為專案找到符合各自情境的解決方法,視情況採取不同方式。